Crates
A crate is the smallest unit of compilation in Rust.
In simple terms:
- A crate = one compiled output
- It can produce:
- an executable → binary crate
- a reusable package → library crate
Modules organize code inside a crate
Crates organize code at the compilation level
Types of Crates
Rust has two main crate types:
| Crate Type | Output | Entry File |
|---|---|---|
| Binary crate | Executable program | main.rs |
| Library crate | Reusable code | lib.rs |
Binary Crate (Executable)
A binary crate:
- produces a runnable executable
- must have a
main()function - starts execution from
main()
Example: Simple Binary Crate
my_app/
├── Cargo.toml
└── src/
└── main.rs
main.rs
fn main() {
println!("Hello from binary crate!");
}
main.rsis the crate rootmain()is the program entry pointcargorun builds and executes this crate
Binary Crate with Modules
src/
├── main.rs
└── utils.rs
main.rs
mod utils;
fn main() {
utils::say_hi();
}
utils.rs
pub fn say_hi() {
println!("Hi from utils module");
}
main.rsis still the crate rootutilsis a module inside the cratepuballows access frommain
Multiple Binary Crates in One Project
Rust allows multiple binaries in one package.
Structure
src/
├── main.rs // default binary
└── bin/
├── server.rs
└── client.rs
Running binaries
cargo run # runs main.rs
cargo run --bin server
cargo run --bin client
- Each file in
src/bin/is a separate binary crate - All share the same
Cargo.toml - Useful for CLI tools, microservices, or test apps
Library Crate (Reusable Code)
A library crate:
- does NOT have
main() - exposes reusable functionality
- is imported by other crates using
use
Example: Simple Library Crate
my_lib/
├── Cargo.toml
└── src/
└── lib.rs
lib.rs
pub fn greet() {
println!("Hello from library crate");
}
lib.rsis the crate rootpubexposes the function to other crates- Built with
cargo build
Using a Library Crate in a Binary Crate
Workspace-like usage (same project)
src/
├── main.rs
└── lib.rs
lib.rs
pub fn add(a: i32, b: i32) -> i32 {
a + b
}
main.rs
use my_project::add;
fn main() {
println!("{}", add(2, 3));
}
my_project= crate name fromCargo.toml- Binary crate automatically depends on the library crate
- Common Rust pattern 👍
Library Crate with Modules
src/
├── lib.rs
├── math/
│ ├── mod.rs
│ └── add.rs
└── utils.rs
lib.rs
pub mod math;
pub mod utils;
math/mod.rs
pub mod add;
math/add.rs
pub fn add(a: i32, b: i32) -> i32 {
a + b
}
Usage in another crate
use my_lib::math::add::add;
Binary + Library Together (Very Common)
This is idiomatic Rust.
Structure
src/
├── main.rs // binary crate
└── lib.rs // library crate
Why do this?
- Put logic in
lib.rs - Keep
main.rsthin - Improves testability and reuse
lib.rs
pub mod auth {
pub fn login() {
println!("User logged in");
}
}
main.rs
use my_app::auth::login;
fn main() {
login();
}
Crate Root Explained
| Crate Type | Crate Root |
|---|---|
| Binary | main.rs |
| Library | lib.rs |
The crate root:
- defines the module tree
- controls what is public
- is where
crate::paths start
crate:: vs External Crates
Inside the same crate
crate::math::add::add();
External crate
use serde::Serialize;
Rust clearly separates:
- internal modules →
crate:: - external crates → crate name
Cargo.toml and Crates
[package]
name = "my_app"
version = "0.1.0"
edition = "2021"
name= crate name used inuse- Dependencies listed under
[dependencies] - Cargo builds each crate independently
How Modules & Crates Fit Together
Think of it like this:
Workspace (optional)
└── Package (Cargo.toml)
├── Crate (binary or library)
│ └── Modules
│ └── Functions / structs
Key Differences: Crate vs Module
| Feature | Crate | Module |
|---|---|---|
| Compiled separately | ✅ | ❌ |
| Has crate root | ✅ | ❌ |
| Controls visibility | ✅ | ✅ |
| Can be published | ✅ | ❌ |
| File-based | Yes | Yes |
When to Use What
- Modules → organize logic inside a crate
- Library crate → reusable core logic
- Binary crate → executable behavior
Rust strongly encourages:
“Write libraries, then write small binaries that use them.”